#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include "tcp_listen.h"
#include "game_log.h"

bool NetworkListen::CancelFd(int fd)
{
	auto itr = _fd_clients.find(fd);
	if (itr == _fd_clients.end())
	{
		return false;
	}
	_fd_clients.erase(itr);
	return true;

}

int NetworkListen::init_accept(struct ev_loop *loop, int port)
{
	GAME_LOG.info("Listening on %d\n",
		port);

	struct sockaddr_in addr;

	_fd = socket(PF_INET, SOCK_STREAM, 0);
	if (_fd < 0)
	{
		GAME_LOG.error("File[%s] Line[%d]: socket failed: %s\n",
			__FILE__, __LINE__, strerror(errno));
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	if (addr.sin_addr.s_addr == INADDR_NONE)
	{
		GAME_LOG.error("game::init_accept Incorrect ip address!");
		close(_fd);
		_fd = -1;
		exit(1);
	}

	int on = 1;
	if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
	{
		GAME_LOG.error("File[%s] Line[%d]: setsockopt failed: %s\n",
			__FILE__, __LINE__, strerror(errno));
		close(_fd);
		return -1;
	}

	if (bind(_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		GAME_LOG.error("File[%s] Line[%d]: bind failed: %s\n",
			__FILE__, __LINE__, strerror(errno));
		close(_fd);
		return -1;
	}

	fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL, 0) | O_NONBLOCK);

	listen(_fd, 10000);

	_ev_accept.data = this;
	ev_io_init(&_ev_accept, NetworkListen::accept_cb, _fd, EV_READ);
	ev_io_start(loop, &_ev_accept);

	GAME_LOG.info("listen ok\n");

	return 0;
}

void NetworkListen::accept_cb(struct ev_loop * loop, ev_io * w, int revents)
{
	if (EV_ERROR & revents)
	{
		GAME_LOG.error("got invalid event\n");
		return;
	}

	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	int fd = accept(w->fd, (struct sockaddr *)&client_addr, &client_len);
	if (fd < 0)
	{
		GAME_LOG.error("accept error[%s]\n", strerror(errno));
		return;
	}

	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);

	NetworkListen *game = (NetworkListen*)(w->data);
	TcpClient *client = game->NewConnect(fd);
	if (client)
	{
		game->_fd_clients[fd] = client;
	}
	else close(fd);
}
